fsck: Mark commits with missing or deleted object partial
authorAlexander Larsson <alexl@redhat.com>
Fri, 6 Apr 2018 13:39:43 +0000 (15:39 +0200)
committerAtomic Bot <atomic-devel@projectatomic.io>
Sat, 14 Apr 2018 15:36:21 +0000 (15:36 +0000)
This means we can later use various operations to heal the repository
because ostree does not assume all objects are there.

This the begining of a fix for https://github.com/ostreedev/ostree/pull/345

Closes: #1533
Approved by: cgwalters

src/ostree/ot-builtin-fsck.c
tests/installed/nondestructive/itest-pull.sh
tests/test-corruption.sh

index 3f9da7835f2e5a09c3cfc3f21226c58bc48d497e..8a44b619a9b7fcfb1b4a7358ca0ff1493e2e129a 100644 (file)
@@ -53,6 +53,8 @@ static gboolean
 fsck_one_object (OstreeRepo            *repo,
                  const char            *checksum,
                  OstreeObjectType       objtype,
+                 GHashTable            *object_parents,
+                 GVariant              *key,
                  gboolean              *out_found_corruption,
                  GCancellable          *cancellable,
                  GError               **error)
@@ -60,12 +62,14 @@ fsck_one_object (OstreeRepo            *repo,
   g_autoptr(GError) temp_error = NULL;
   if (!ostree_repo_fsck_object (repo, objtype, checksum, cancellable, &temp_error))
     {
+      gboolean object_missing = FALSE;
+
       if (g_error_matches (temp_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
         {
           g_clear_error (&temp_error);
           g_printerr ("Object missing: %s.%s\n", checksum,
                       ostree_object_type_to_string (objtype));
-          *out_found_corruption = TRUE;
+          object_missing = TRUE;
         }
       else
         {
@@ -73,7 +77,7 @@ fsck_one_object (OstreeRepo            *repo,
             {
               g_printerr ("%s\n", temp_error->message);
               (void) ostree_repo_delete_object (repo, objtype, checksum, cancellable, NULL);
-              *out_found_corruption = TRUE;
+              object_missing = TRUE;
             }
           else
             {
@@ -81,6 +85,26 @@ fsck_one_object (OstreeRepo            *repo,
               return FALSE;
             }
         }
+
+      if (object_missing)
+        {
+          *out_found_corruption = TRUE;
+
+          if (object_parents != NULL && objtype != OSTREE_OBJECT_TYPE_COMMIT)
+            {
+              g_auto(GStrv) parent_commits =  ostree_repo_traverse_parents_get_commits (object_parents, key);
+              int i;
+
+              /* The commit was missing or deleted, mark the commit partial */
+              for (i = 0; parent_commits[i] != NULL; i++)
+                {
+                  const char *parent_commit = parent_commits[i];
+                  g_printerr ("Marking commit %s as partial\n", parent_commit);
+                  if (!ostree_repo_mark_commit_partial (repo, parent_commit, TRUE, error))
+                    return FALSE;
+                }
+            }
+        }
     }
 
   return TRUE;
@@ -94,6 +118,7 @@ fsck_reachable_objects_from_commits (OstreeRepo            *repo,
                                      GError               **error)
 {
   g_autoptr(GHashTable) reachable_objects = ostree_repo_traverse_new_reachable ();
+  g_autoptr(GHashTable) object_parents = ostree_repo_traverse_new_parents ();
 
   GHashTableIter hash_iter;
   gpointer key, value;
@@ -108,8 +133,8 @@ fsck_reachable_objects_from_commits (OstreeRepo            *repo,
 
       g_assert (objtype == OSTREE_OBJECT_TYPE_COMMIT);
 
-      if (!ostree_repo_traverse_commit_union (repo, checksum, 0, reachable_objects,
-                                              cancellable, error))
+      if (!ostree_repo_traverse_commit_union_with_parents (repo, checksum, 0, reachable_objects, object_parents,
+                                                           cancellable, error))
         return FALSE;
     }
 
@@ -127,8 +152,9 @@ fsck_reachable_objects_from_commits (OstreeRepo            *repo,
 
       ostree_object_name_deserialize (serialized_key, &checksum, &objtype);
 
-      if (!fsck_one_object (repo, checksum, objtype, out_found_corruption,
-                            cancellable, error))
+      if (!fsck_one_object (repo, checksum, objtype,
+                            object_parents, serialized_key,
+                            out_found_corruption, cancellable, error))
         return FALSE;
 
       i++;
@@ -150,7 +176,7 @@ fsck_commit_for_ref (OstreeRepo    *repo,
                      GError       **error)
 {
   if (!fsck_one_object (repo, checksum, OSTREE_OBJECT_TYPE_COMMIT,
-                        found_corruption,
+                        NULL, NULL, found_corruption,
                         cancellable, error))
     return FALSE;
 
index a7cd922d4d588098aaf99555dc1915575a15cb0f..b3a52a27d37568be801f163313d8330868e16369 100755 (executable)
@@ -30,6 +30,20 @@ ostree --repo=bare-repo init --mode=bare-user
 ostree --repo=bare-repo remote add origin --set=gpg-verify=false $(cat ${test_tmpdir}/httpd-address)
 log_timestamps ostree --repo=bare-repo pull --disable-static-deltas origin ${host_nonremoteref}
 
+echo "ok pull"
+
+# fsck marks commits partial
+# https://github.com/ostreedev/ostree/pull/1533
+for d in $(find bare-repo/objects/ -maxdepth 1 -type d); do
+    (find ${d} -name '*.file' || true) | head -20 | xargs rm -vf
+done
+if ostree --repo=bare-repo fsck; then
+    fatal "fsck unexpectedly succeeded"
+fi
+ostree --repo=bare-repo pull origin ${host_nonremoteref}
+# Don't need a full fsck here
+ostree --repo=bare-repo ls origin:${host_nonremoteref} >/dev/null
+
 rm bare-repo repo -rf
 
 # Try copying the host's repo across a mountpoint for direct
@@ -44,6 +58,7 @@ log_timestamps ostree --repo=repo fsck
 cd ..
 umount mnt
 
+# Cleanup
 kill -TERM $(cat ${test_tmpdir}/httpd-pid)
 echo "ok"
 date
index 997d39c7f7a2c1079c9cbf2333231852300f4fa1..3b4a649eafcc9528be6f5716409bf6fa0ad845db 100755 (executable)
@@ -21,7 +21,7 @@
 
 set -euo pipefail
 
-echo "1..6"
+echo "1..8"
 
 . $(dirname $0)/libtest.sh
 
@@ -89,3 +89,43 @@ if ${CMD_PREFIX} ostree --repo=ostree-path-traverse/repo checkout pathtraverse-t
 fi
 assert_file_has_content_literal err.txt 'Invalid / in filename ../afile'
 echo "ok path traverse checkout"
+
+cd ${test_tmpdir}
+rm repo files -rf
+setup_test_repository "bare"
+
+rev=$($OSTREE rev-parse test2)
+
+filechecksum=$(ostree_file_path_to_checksum repo test2 /firstfile)
+rm repo/$(ostree_checksum_to_relative_object_path repo $filechecksum)
+
+assert_not_has_file repo/state/${rev}.commitpartial
+
+if $OSTREE fsck -q 2>err.txt; then
+    assert_not_reached "fsck unexpectedly succeeded"
+fi
+assert_file_has_content_literal err.txt "Object missing:"
+assert_file_has_content_literal err.txt "Marking commit $rev as partial"
+assert_has_file repo/state/${rev}.commitpartial
+
+echo "ok missing file"
+
+cd ${test_tmpdir}
+rm repo files -rf
+setup_test_repository "bare"
+
+rev=$($OSTREE rev-parse test2)
+
+filechecksum=$(ostree_file_path_to_checksum repo test2 /firstfile)
+echo corrupted >> repo/$(ostree_checksum_to_relative_object_path repo $filechecksum)
+
+assert_not_has_file repo/state/${rev}.commitpartial
+
+if $OSTREE fsck -q --delete 2>err.txt; then
+    assert_not_reached "fsck unexpectedly succeeded"
+fi
+assert_file_has_content_literal err.txt "Corrupted file object;"
+assert_file_has_content_literal err.txt "Marking commit $rev as partial"
+assert_has_file repo/state/${rev}.commitpartial
+
+echo "ok corrupt file"